#pragma once
#include"Common.hpp"

const std::string DEFAULTSTR = "Log.txt";
enum
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL,
    _SCREEN_TYPE_,
    _FILE_TYPE
};
struct Logmessage
{
    std::string _level; // 等级
    pid_t _pid;
    std::string _filename;   // 文件名
    int _filenumber;         // 行号
    std::string _curr_time;  // 获取日志信息出现的时间
    std::string _logmessage; // 信息
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
        break;
    case INFO:
        return "INFO";
        break;
    case WARNING:
        return "WARNING";
        break;
    case ERROR:
        return "ERROR";
        break;
    case FATAL:
        return "FATAL";
        break;
    default:
        return "UNKNOW";
        break;
    }
}
std::string GetCurrTime()
{
    time_t now_time = time(nullptr);
    struct tm *curr_time = localtime(&now_time);
    // 转换为string格式
    char buf[128];
    snprintf(buf, sizeof(buf), "%d-%02d-%02d %02d:%02d:%02d",
             curr_time->tm_year + 1900, curr_time->tm_mon + 1, curr_time->tm_mday, curr_time->tm_hour, curr_time->tm_min, curr_time->tm_sec);
    return buf;
}
class Log
{
    void LogMsgToScreen(const Logmessage &logmsg)
    {
        printf("[%s][%d][%s][%d][%s] %s",
               logmsg._level.c_str(),
               logmsg._pid, logmsg._filename.c_str(),
               logmsg._filenumber,
               logmsg._curr_time.c_str(),
               logmsg._logmessage.c_str());
    }
    void LogMsgToFile(const Logmessage &logmsg)
    {
        char buf_info[2048] = "\0";
        snprintf(buf_info, sizeof(buf_info), "[%s][%d][%s][%d][%s] %s",
                 logmsg._level.c_str(),
                 logmsg._pid, logmsg._filename.c_str(),
                 logmsg._filenumber, logmsg._curr_time.c_str(),
                 logmsg._logmessage.c_str());
        /*--系统调用版本--*/
        // int fd = open(_logfile.c_str(),O_CREAT | O_WRONLY | O_APPEND,0666);
        // if(fd < 0)
        // {
        //     perror("open");
        //     return;
        // }
        // write(fd,buf_info,sizeof(buf_info));

        /*--c++提供的fstream--*/
        std::ofstream out;
        out.open(_logfile, std::ios::out | std::ios::app | std::ios::binary);
        if (!out.is_open())
            return;
        out.write(buf_info, sizeof(buf_info));
        out.close();
    }
    void FlushLogMsg(int level, const Logmessage &logmsg)
    {
        // c++11的锁
        //  _mutex.lock();
        // RAII类型的锁
        std::unique_lock<std::mutex> lock(_mtx);
        switch (_type)
        {
        case _SCREEN_TYPE_:
            LogMsgToScreen(logmsg);
            break;
        case _FILE_TYPE:
            LogMsgToFile(logmsg);
            break;
        }
    }
    Log(const std::string &logfile = DEFAULTSTR) : _logfile(logfile), _type(_SCREEN_TYPE_)
    {
    }

public:
    Log(const Log &lg) = delete;
    Log(Log &lg) = delete;
    Log(Log &&lg) = delete;
    void ModPrintFormat(int type)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _type = type;
    }
    void LogMessage(int level, std::string filename, int filenumber, const char *format, ...) // 注意-->可变函数参数
    {
        if (level == DEBUG && _isopen)
            return;
        Logmessage msg;
        msg._level = LevelToString(level);
        msg._filename = filename;
        msg._pid = getpid();
        msg._filenumber = filenumber;
        msg._curr_time = GetCurrTime();
        // 注意：取出可变参数的固定写法
        va_list _ap;           // 创建变量,本质是一个指针
        va_start(_ap, format); // 将参数列表中离...最近的确定的参数传入
        char log_info[512];
        vsnprintf(log_info, sizeof(log_info), format, _ap);
        va_end(_ap); // 销毁_ap
        msg._logmessage = log_info;
        FlushLogMsg(level, msg);
    }
    ~Log()
    {
    }
    void EnableFiltration(bool flag)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _isopen = flag;
    }
    static Log *GetInstance(const std::string &logfile = DEFAULTSTR)
    {
        // 双重检查锁定模式
        if (_lg == nullptr)
        {
            std::unique_lock<std::mutex> lock(_static_mtx);
            if (_lg == nullptr)
            {
                _lg = new Log(logfile);
            }
        }
        return _lg;
    }

private:
    static Log *_lg;
    int _type;
    std::string _logfile;
    std::mutex _mtx;
    static std::mutex _static_mtx;
    bool _isopen = false;
};

std::mutex Log::_static_mtx;
Log *Log::_lg = nullptr;
static std::unique_ptr<Log> _ptr(Log::GetInstance());
// 打开过滤器
#define EnabelFILTRATION()            \
    do                                \
    {                                 \
        _ptr->EnableFiltration(true); \
    } while (0)
// 关闭过滤器
#define ClOSEFILTRATION                \
    do                                 \
    {                                  \
        _ptr->EnableFiltration(false); \
    } while (0)
#define LOG(level, format, ...)                                             \
    do                                                                      \
    {                                                                       \
        _ptr->LogMessage(level, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0)

#define LOGTOSCREEN(level, format, ...)                                     \
    do                                                                      \
    {                                                                       \
        _ptr->ModPrintFormat(_SCREEN_TYPE_);                                \
        _ptr->LogMessage(level, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0)

#define LOGTOFILE(level)                  \
    do                                    \
    {                                     \
        _ptr->ModPrintFormat(_FILE_TYPE); \
    } while (0)                